import sys
sys.path.append("..")
import pyrtl
from  pyrtl import GPUSim
import or1200_definitions

OR1200_QMEMFSM_IDLE = pyrtl.Const(0, bitwidth=3)
OR1200_QMEMFSM_STORE = pyrtl.Const(1, bitwidth=3)
OR1200_QMEMFSM_LOAD = pyrtl.Const(2, bitwidth=3)
OR1200_QMEMFSM_FETCH = pyrtl.Const(3, bitwidth=3)

dw = or1200_definitions.OR1200_OPERAND_WIDTH

class Or1200QmemTop(object):
    def __init__(self):
        # Embedded memory
        ####################################
        # module connection
        ####################################

        # Clock and reset
        self.rst = pyrtl.Input(bitwidth=1, name='rst')

        # QMEM and CPU/IMMU
        self.qmemimmu_adr_i = pyrtl.Input(bitwidth=32, name='Or1200QmemTop_qmemimmu_adr_i')
        self.qmemimmu_cycstb_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_qmemimmu_cycstb_i')
        self.qmemimmu_ci_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_qmemimmu_ci_i')
        self.qmemicpu_sel_i = pyrtl.Input(bitwidth=4, name='Or1200QmemTop_qmemicpu_sel_i')
        self.qmemicpu_tag_i = pyrtl.Input(bitwidth=4, name='Or1200QmemTop_qmemicpu_tag_i')

        self.qmemicpu_dat_o = pyrtl.Output(bitwidth=32, name='Or1200QmemTop_qmemicpu_dat_o')
        self.qmemicpu_ack_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_qmemicpu_ack_o')
        self.qmemimmu_rty_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_qmemimmu_rty_o')
        self.qmemimmu_err_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_qmemimmu_err_o')
        self.qmemimmu_tag_o = pyrtl.Output(bitwidth=4, name='Or1200QmemTop_qmemimmu_tag_o')

        # QMEM and IC
        self.icqmem_adr_o = pyrtl.Output(bitwidth=32, name='Or1200QmemTop_icqmem_adr_o')
        self.icqmem_cycstb_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_icqmem_cycstb_o')
        self.icqmem_ci_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_icqmem_ci_o')
        self.icqmem_sel_o = pyrtl.Output(bitwidth=4, name='Or1200QmemTop_icqmem_sel_o')
        # self.icqmem_tag_o = pyrtl.Output(bitwidth=4, name='icqmem_tag_o')
        self.icqmem_tag_o = pyrtl.WireVector(bitwidth=4, name='Or1200QmemTop_icqmem_tag_o')
        self.icqmem_dat_i = pyrtl.Input(bitwidth=32, name='Or1200QmemTop_icqmem_dat_i')
        self.icqmem_ack_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_icqmem_ack_i')
        # self.icqmem_rty_i = pyrtl.Input(bitwidth=1, name='icqmem_rty_i')
        self.icqmem_rty_i = pyrtl.WireVector(bitwidth=1, name='Or1200QmemTop_icqmem_rty_i')
        self.icqmem_err_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_icqmem_err_i')
        # self.icqmem_tag_i = pyrtl.Input(bitwidth=4, name='icqmem_tag_i')
        self.icqmem_tag_i = pyrtl.WireVector(bitwidth=4, name='Or1200QmemTop_icqmem_tag_i')

        # QMEM and CPU/DMMU
        self.qmemdmmu_adr_i = pyrtl.Input(bitwidth=32, name='Or1200QmemTop_qmemdmmu_adr_i')
        self.qmemdmmu_cycstb_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_qmemdmmu_cycstb_i')
        self.qmemdmmu_ci_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_qmemdmmu_ci_i')
        self.qmemdcpu_we_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_qmemdcpu_we_i')
        self.qmemdcpu_sel_i = pyrtl.Input(bitwidth=4, name='Or1200QmemTop_qmemdcpu_sel_i')
        self.qmemdcpu_tag_i  =pyrtl.Input(bitwidth=4, name='Or1200QmemTop_qmemdcpu_tag_i')
        self.qmemdcpu_dat_i = pyrtl.Input(bitwidth=32, name='Or1200QmemTop_qmemdcpu_dat_i')
        self.qmemdcpu_dat_o = pyrtl.Input(bitwidth=32, name='Or1200QmemTop_qmemdcpu_dat_o')
        self.qmemdcpu_ack_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_qmemdcpu_ack_o')
        self.qmemdcpu_rty_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_qmemdcpu_rty_o')
        self.qmemdmmu_err_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_qmemdmmu_err_o')
        self.qmemdmmu_tag_o = pyrtl.Output(bitwidth=4, name='Or1200QmemTop_qmemdmmu_tag_o')

        # QMEM and DC
        self.dcqmem_adr_o = pyrtl.Output(bitwidth=32,name='Or1200QmemTop_dcqmem_adr_o')
        self.dcqmem_cycstb_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_dcqmem_cycstb_o')
        self.dcqmem_ci_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_dcqmem_ci_o')
        self.dcqmem_we_o = pyrtl.Output(bitwidth=1, name='Or1200QmemTop_dcqmem_we_o')
        self.dcqmem_sel_o = pyrtl.Output(bitwidth=4, name='Or1200QmemTop_dcqmem_sel_o')
        self.dcqmem_tag_o = pyrtl.Output(bitwidth=4, name='Or1200QmemTop_dcqmem_tag_o')
        self.dcqmem_dat_o = pyrtl.Output(bitwidth=dw, name='Or1200QmemTop_dcqmem_dat_o')

        self.dcqmem_dat_i = pyrtl.Input(bitwidth=dw, name='Or1200QmemTop_dcqmem_dat_i')
        self.dcqmem_ack_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_dcqmem_ack_i')
        # self.dcqmem_rty_i = pyrtl.Input(bitwidth=1, name='dcqmem_rty_i')
        self.dcqmem_rty_i = pyrtl.WireVector(bitwidth=1, name='Or1200QmemTop_dcqmem_rty_i')
        self.dcqmem_err_i = pyrtl.Input(bitwidth=1, name='Or1200QmemTop_dcqmem_err_i')
        self.dcqmem_tag_i = pyrtl.Input(bitwidth=4, name='Or1200QmemTop_dcqmem_tag_i')

        # Internal regs and wires
        self.iaddr_qmem_hit = pyrtl.WireVector(bitwidth=1, name='Or1200QmemTop_iaddr_qmem_hit')
        self.daddr_qmem_hit = pyrtl.WireVector(bitwidth=1, name='Or1200QmemTop_daddr_qmem_hit')
        self.state = pyrtl.Register(bitwidth=3, name='Or1200QmemTop_state')
        self.qmem_dack = pyrtl.Register(bitwidth=1, name='Or1200QmemTop_qmem_dack')
        self.qmem_iack = pyrtl.Register(bitwidth=1, name='Or1200QmemTop_qmem_iack')
        self.qmem_di = pyrtl.WireVector(bitwidth=32, name='Or1200QmemTop_qmem_di')
        self.qmem_do = pyrtl.WireVector(bitwidth=32, name='Or1200QmemTop_qmem_do')
        self.qmem_en = pyrtl.WireVector(bitwidth=1, name='Or1200QmemTop_qmem_en')
        self.qmem_we = pyrtl.WireVector(bitwidth=1, name='Or1200QmemTop_qmem_we')

        # OR1200_QMEM_BSEL = False
        self.qmem_addr = pyrtl.WireVector(bitwidth=32, name='Or1200QmemTop_qmem_addr')
        # OR1200_QMEM_ACK = False
        self.qmem_ack = pyrtl.Const(0b1, bitwidth=1)

        #initialize all the parts
        leafAdded_1 = LeafAdded_1()
        leafAdded_2 = LeafAdded_2()
        qMEMConFSM = QMEMConFSM()

        #establish connection relations for leaf added -1
        self.icqmem_tag_i <<= leafAdded_1.icqmem_tag_i
        leafAdded_1.icqmem_err_i = self.icqmem_err_i
        leafAdded_1.icqmem_tag_o = self.icqmem_tag_o

        # leaf added-1
        self.icqmem_rty_i <<= (~self.icqmem_ack_i) & (~self.icqmem_err_i)

        # establish connection relations for leaf added -2
        self.icqmem_tag_i = leafAdded_2.icqmem_tag_i
        leafAdded_2.icqmem_err_i <<= self.icqmem_err_i
        leafAdded_2.icqmem_tag_o <<= self.icqmem_tag_o

        # leaf added-2
        self.dcqmem_rty_i <<= ~self.dcqmem_ack_i

        # QMEM and CPU/IMMU
        self.qmemicpu_dat_o <<= pyrtl.select(self.qmem_iack, self.qmem_do, self.icqmem_dat_i)
        self.qmemicpu_ack_o = pyrtl.select(self.qmem_iack, pyrtl.Const(0b1, bitwidth=1), self.icqmem_ack_i)
        self.qmemimmu_rty_o = pyrtl.select(self.qmem_iack, pyrtl.Const(0b0, bitwidth=1), self.icqmem_rty_i)
        self.qmemimmu_err_o = pyrtl.select(self.qmem_iack, pyrtl.Const(0b0, bitwidth=1), self.icqmem_err_i)
        self.qmemimmu_tag_o = pyrtl.select(self.qmem_iack, pyrtl.Const(0b0, bitwidth=1), self.icqmem_tag_i)

        # QMEM and IC
        self.icqmem_adr_o = pyrtl.select(self.iaddr_qmem_hit, pyrtl.Const(0x0, bitwidth=32), self.qmemimmu_adr_i)
        self.icqmem_cycstb_o = pyrtl.select(self.iaddr_qmem_hit, pyrtl.Const(0x0, bitwidth=1), self.qmemimmu_cycstb_i)
        self.icqmem_ci_o = pyrtl.select(self.iaddr_qmem_hit, pyrtl.Const(0x0, bitwidth=1), self.qmemimmu_ci_i)
        self.icqmem_sel_o = pyrtl.select(self.iaddr_qmem_hit, pyrtl.Const(0x0, bitwidth=1), self.qmemicpu_sel_i)
        self.icqmem_tag_o = pyrtl.select(self.iaddr_qmem_hit, pyrtl.Const(0x0, bitwidth=1), self.qmemicpu_tag_i)

        # QMEM and CPU/DMMU
        self.qmemdcpu_dat_o = pyrtl.select(self.daddr_qmem_hit, self.qmem_do, self.dcqmem_dat_i)
        self.qmemdcpu_ack_o = pyrtl.select(self.daddr_qmem_hit, self.qmem_dack, self.dcqmem_ack_i)
        self.qmemdcpu_rty_o = pyrtl.select(self.daddr_qmem_hit, ~self.qmem_dack, self.dcqmem_rty_i)
        self.qmemdmmu_err_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0b0, bitwidth=1), self.dcqmem_err_i)
        self.qmemdmmu_tag_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=4), self.dcqmem_tag_i)

        # QMEM and DC
        self.dcqmem_adr_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=32), self.qmemdmmu_adr_i)
        self.dcqmem_cycstb_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=1), self.qmemdmmu_cycstb_i)
        self.dcqmem_ci_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=1), self.qmemdmmu_ci_i)
        self.dcqmem_we_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=1), self.qmemdcpu_we_i)
        self.dcqmem_sel_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=4), self.qmemdcpu_sel_i)
        self.dcqmem_tag_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=4), self.qmemdcpu_tag_i)
        self.dcqmem_dat_o = pyrtl.select(self.daddr_qmem_hit, pyrtl.Const(0, bitwidth=32), self.qmemdcpu_dat_i)

        # Address comparison whether QMEM was hit
        # OR1200_QMEM_IADDR = False
        self.iaddr_qmem_hit = pyrtl.Const(0b0, bitwidth=1)

        # OR1200_QMEM_DADDR = False
        self.daddr_qmem_hit = pyrtl.Const(0b0, bitwidth=1)

        self.qmem_en = self.iaddr_qmem_hit & self.qmemimmu_cycstb_i | self.daddr_qmem_hit & self.qmemdmmu_cycstb_i
        self.qmem_we = self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmemdcpu_we_i

        # OR1200_QMEM_BSEL = False
        self.qmem_di = self.qmemdcpu_dat_i
        self.qmem_addr = pyrtl.select(self.qmemdmmu_cycstb_i & self.daddr_qmem_hit, self.qmemdmmu_adr_i, self.qmemimmu_adr_i)

        # establish connection relations for QMEM control FSM
        self.state.next <<= qMEMConFSM.state
        self.qmem_iack.next <<= qMEMConFSM.qmem_iack
        self.qmem_dack.next <<= qMEMConFSM.qmem_dack
        qMEMConFSM.rst <<= self.rst
        qMEMConFSM.qmemdmmu_cycstb_i <<= self.qmemdmmu_cycstb_i
        qMEMConFSM.daddr_qmem_hit <<= self.daddr_qmem_hit
        qMEMConFSM.qmemdcpu_we_i <<= self.qmemdcpu_we_i
        # qMEMConFSM.qmem_ack <<= self.qmem_ack
        qMEMConFSM.qmemimmu_cycstb_i <<= self.qmemimmu_cycstb_i
        qMEMConFSM.iaddr_qmem_hit <<= self.iaddr_qmem_hit



# leaf added_1
class LeafAdded_1(object):
    def __init__(self):
        self.icqmem_err_i = pyrtl.WireVector(bitwidth=1, name='LeafAdded_1_icqmem_err_i')
        self.icqmem_tag_o = pyrtl.WireVector(bitwidth=4, name='LeafAdded_1_icqmem_tag_o')

        self.icqmem_tag_i = pyrtl.WireVector(bitwidth=4, name='LeafAdded_1_icqmem_tag_i')

        with pyrtl.conditional_assignment:
            with self.icqmem_err_i == pyrtl.Const(0b1, bitwidth=1):
                self.icqmem_tag_i |= or1200_definitions.OR1200_ITAG_BE
            with pyrtl.otherwise:
                self.icqmem_tag_i |= self.icqmem_tag_o


# leaf added_2
class LeafAdded_2(object):
    def __init__(self):
        self.icqmem_err_i = pyrtl.WireVector(bitwidth=1, name='LeafAdded_2_icqmem_err_i')
        self.icqmem_tag_o = pyrtl.WireVector(bitwidth=4, name='LeafAdded_2_icqmem_tag_o')

        self.icqmem_tag_i = pyrtl.WireVector(bitwidth=4, name='LeafAdded_2_icqmem_tag_i')

        with pyrtl.conditional_assignment:
            with self.icqmem_err_i == pyrtl.Const(0b1, bitwidth=1):
                self.icqmem_tag_i |= or1200_definitions.OR1200_DTAG_BE
            with pyrtl.otherwise:
                self.icqmem_tag_i |= self.icqmem_tag_o

# QMEM control FSM
class QMEMConFSM(object):
    def __init__(self):
        self.rst = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_rst')
        self.state = pyrtl.WireVector(bitwidth=3, name='QMEMConFSM_state')
        self.qmem_dack = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_qmem_dack')
        self.qmem_iack = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_qmem_iack')
        self.qmemdmmu_cycstb_i = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_qmemdmmu_cycstb_i')
        self.daddr_qmem_hit = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_daddr_qmem_hit')
        self.qmemdcpu_we_i = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_qmemdcpu_we_i')
        self.qmem_ack = pyrtl.Const(0b1, bitwidth=1)
        self.qmemimmu_cycstb_i = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_qmemimmu_cycstb_i')
        self.iaddr_qmem_hit = pyrtl.WireVector(bitwidth=1, name='QMEMConFSM_iaddr_qmem_hit')

        with pyrtl.conditional_assignment:
            with self.rst:
                self.state |= OR1200_QMEMFSM_IDLE
                self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
                self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
            with self.state.__eq__(OR1200_QMEMFSM_IDLE):
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmemdcpu_we_i & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_STORE
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_LOAD
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemimmu_cycstb_i & self.iaddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_FETCH
                    self.qmem_iack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
            with self.state.__eq__(OR1200_QMEMFSM_STORE):
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmemdcpu_we_i & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_STORE
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_LOAD
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemimmu_cycstb_i & self.iaddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_FETCH
                    self.qmem_iack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
                with pyrtl.otherwise:
                    self.state |= OR1200_QMEMFSM_IDLE
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
            with self.state.__eq__(OR1200_QMEMFSM_LOAD):
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmemdcpu_we_i & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_STORE
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_LOAD
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemimmu_cycstb_i & self.iaddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_FETCH
                    self.qmem_iack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
                with pyrtl.otherwise:
                    self.state |= OR1200_QMEMFSM_IDLE
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
            with self.state.__eq__(OR1200_QMEMFSM_FETCH):
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmemdcpu_we_i & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_STORE
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemdmmu_cycstb_i & self.daddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_LOAD
                    self.qmem_dack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                with (self.qmemimmu_cycstb_i & self.iaddr_qmem_hit & self.qmem_ack):
                    self.state |= OR1200_QMEMFSM_FETCH
                    self.qmem_iack |= pyrtl.Const(0b1, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
                with pyrtl.otherwise:
                    self.state |= OR1200_QMEMFSM_IDLE
                    self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)
                    self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
            with pyrtl.otherwise:
                self.state |= OR1200_QMEMFSM_IDLE
                self.qmem_dack |= pyrtl.Const(0b0, bitwidth=1)
                self.qmem_iack |= pyrtl.Const(0b0, bitwidth=1)

if __name__ == '__main__':
    or1200QmemTop = Or1200QmemTop()
    sim = pyrtl.GPUSim_now.GPUSim(65536)
    sim.create_dll('qmemtop.cu')
    # print(pyrtl.working_block())
    # com = pyrtl.CompiledSimulation()
    # f = open("com.c", 'w')
    # com._create_code(lambda s: f.write(s + '\n'))
    # f.close()
    # essent = pyrtl.ESSENT()
    # ff = open("out.c", 'w')
    # essent._create_code(lambda s: ff.write(s + '\n'))
    # ff.close()
